#!/usr/bin/env python
#
# Zenmp3 - a music downloader for YouTube.
#
# Copyright Klaus Zimmermann 2012 - 2016 all rights reserved.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#

'''
USAGE:
This file should run standalone in most Python instalations, but if
you would like to use the command-line interface, use it as follows:

zenmp3 [URL] [SONGNAME]

You may also run zenmp3 --help for more information.

Version history (since migration from Google Code):

0.9 BETA - Started work on Mac support.
0.10 BETA - Program now defaults downloads to OGG format with the option of
            MP3 as a secondary option. Viva the free formats!
0.11 - You are now able to search for music from *within* the program without
       even having to access youtube.com thanks to the Searx search engine
            
TODO: 

 - Test and handle each one of the following cases:
    1. User provides a nonexistent video url.
    2. User provides a link to a playlist instead of single video.
    3. User's download is cancelled either manually or not.
    4. User encounters a problem while converting downloaded video.
 
 - Provide a way to manually cancel a download or conversion process.

 - Provide a way to "preview" the remaining time for the process to finish

 - Keybindings and menu options, instead of simple Alt-Letter shortcuts.

 - Thinking waaaaay ahead, why not enable batch conversion?
'''

VERSION = "0.11 BETA"
converter = "unavailable"

import os
import sys 
import time
import json
import urllib
import webbrowser
import subprocess as sp
from random import randint

if sys.platform == "darwin":
    OS_TYPE = "mac"
elif sys.platform == "win32":
    OS_TYPE = "windows"
else:
    OS_TYPE = "linux"

# GUI toolkit detection
try:
    import gtk
    hasGTK = True
    hasTK = False
    hasgui = True
    guiframe = "GTK"
except ImportError: # No GTK+; Windows, maybe?
    try:
        import Tkinter as tk
        hasGTK = False
        hasTK = True
        hasgui = True
        guiframe = "TK"
    except ImportError: # Python3
        try:
            import tkinter as tk
            hasgui = True
            hasGTK = False
            hasTK = True
            guiframe = "TK"
        except ImportError: # No supported toolkits found. To the CLI.
            sys.stderr.write("Unable to launch GUI. Falling back to command-line mode...\nHave your options ready!\n")
            hasgui = False

# A few command-line legacy thangs
if len(sys.argv) == 1 and hasgui:
    pass
elif len(sys.argv) == 1:
    # Fallback to CLI and Error: missing arguments
    print("Error: Missing arguments.")
    print("Try %s --help for more information" % sys.argv[0])
    sys.exit(1)
else:
    # We are forcefully running in command-line mode (which MUST work as well!)
    hasgui = False
    if '--help' in sys.argv or '-h' in sys.argv:
        # Display help options and exit
        print("Zenmp3 - a free YouTube to MP3 converter (Python Version)")
        print("Usage: %s [URL] [SONG NAME]" % sys.argv[0])
        print("Downloads the song from [URL] and saves it as [SONG NAME]")
        print("Options are:")
        print("-h, --help       Displays this help message")
        print("-v, --version    Displays the program version\n\n")
        sys.exit(0)
    elif '--version' in sys.argv or '-v' in sys.argv:
        # Display program version and exit
        print("zenmp3 %s" % VERSION)
        sys.exit(0)
    elif len(sys.argv) == 3:
        # Retrieve the given arguments.
        URL = sys.argv[1]
        SONGNAME = sys.argv[2]
    elif len(sys.argv) > 3:
        # Error: too many arguments
        print("Sorry, we can only download one video at a time.")
        sys.exit(1)
    else:
        # Invalid options...
        print("There are invalid options in %s" % sys.argv[1:len(sys.argv)])
        print("Try %s --help for more information" % sys.argv[0])
        sys.exit(1)
    
def validator(dependency):
    """
    This function searches the local UNIX $PATH directories for the 
    dependencies (youtube-dl and ffmpeg).

    It's out of the scope of this function to determine if the versions
    of youtube-dl have been updated.
    """
    if OS_TYPE == 'windows':
        satisfied = True # We ship with all dependencies, remember?
    elif OS_TYPE == 'linux' or OS_TYPE == 'mac':
        # Truth is I'm not quite sure about how executables are laid out on a
        # Mac. If they follow the POSIX rules, a PATH should be here.
        # Update: yes, they do. Hooray!
        execpath = os.environ['PATH'].split(":")
        for directory in execpath:
            if dependency in os.listdir(directory):
                satisfied = True
                break
            else:
                satisfied = False
    return satisfied

def warn_old_version():
    """
    As youtube-dl is in constant development to match the ever-changing YouTube
    API, new versions are released frequently, and can age pretty quickly. As
    a good practice, you should update it every week to be able to download the
    latest videos. 

    This function will simply remind the user about it, as there isn't a way 
    that we can predict how the user installed the package.
    """
    checker = sp.Popen(["youtube-dl", "--version"], stdout = sp.PIPE)
    while checker.poll() == None:
        while gtk.events_pending():
            gtk.main_iteration()
    latest_version = checker.stdout.read().strip("\n")

    print("Your youtube-dl version: %s" % latest_version)

    version_date = time.strptime(latest_version, "%Y.%m.%d")
    today = time.strptime(time.strftime("%Y.%m.%d"),"%Y.%m.%d")

    # might be negative if today is Jan 1st
    days_old = today.tm_yday - version_date.tm_yday 

    if days_old >= 14 or -351 < days_old < 0:
        return "Warning: update youtube-dl to ensure maximum compatibility"
    else:
        return "Zenmp3 is up to date and ready to go."

def sanitycheck():
    """
    This function will assess whether the current machine is able to run
    Zenmp3 or not. Essentially, it checks for two things:
    
    1. Whether youtube-dl exists.
    2. Whether there is some kind of encoding converter or not.

    We assume here that graphical toolkit is irrelevant - zenmp3 will work in
    both CLI and GUI modes just fine.
    
    UPDATE: if we're using Windows, we need to add an exception - all windows
    Binaries will be shipped within the zenmp3 directory itself by default,
    so there shouldn't be a need to validate, unless the user has messed with
    our files...
    """
    global converter
    hasYTD = validator("youtube-dl")
    hasFMP = validator("ffmpeg")
    converter = "ffmpeg"
    # Ubuntu 14.04 and beyond has ditched ffmpeg for a similar program, avconv,
    # that is found in the libav package. 
    # Perhaps it's worthy to check one more time?
    if not hasFMP:
        hasFMP = validator("avconv")
        converter = "avconv"
    return hasYTD and hasFMP

def search_song(token, site="https://searx.me"):
    '''
    Pass a search string to search https://searx.me
    Returns a list of dictionaries containing the search results.
    
    Alternatively, pass the address of a Searx instance of your liking as the
    second argument if you don't happen to like searx.me

    '''
    mode = { 
        'q': token + "+site:youtube.com", 
        "categories": 'general',
        "format": 'json'
    }
    url = site + '?' + urllib.urlencode(mode)
    print("Sent query: " + url)

    try:
        results = json.loads(urllib.urlopen(url).read())
    except ValueError:
        print("Request limit exceeded! Please try again in a few minutes")
        return {"title": "Request rate exceeeded.", "url": "Try again later" }

    return results["results"]


#--- The downloader object

class Download():
    '''
    This class represents a download mechanism that can (at least in theory)
    be attached to any kind of interface (graphical, web, CLI...)

    The fetch() method is an abstraction for youtube-dl and a media converter
    mechanism. It can be either ffmpeg or avconv, for their usage is identical.

    The clean() method will remove temporary files that were used by the
    downloader.
    '''
    def __init__(self, system, address, target, directory=None, use_mp3=False):
        '''
        Contextualize everything so that we know exactly how to proceed and
        also prevent things from becoming spaghetti.
        '''
        self.system = system
        self.address = address
        self.use_mp3 = use_mp3
        if self.use_mp3:
            self.extension = '.mp3'
        else:
            self.extension = '.ogg'

        if target.endswith(self.extension):
            self.target = target
        else:
            self.target =  target + self.extension
            
        if self.system == 'linux' or self.system == 'mac':
            self.tempfile = ".youtube-dl_%s.flv" % randint(10000, 99999)
            self.targetdir = directory
            if not self.targetdir.endswith('/'):
                self.targetdir += '/'
            os.chdir(self.targetdir)
        elif self.system == "windows":
            # Hint: Windows binaries will all be shipped together
            # in the same directory structure.
            print("Hey, Windows support is still limited, but we're trying our best!")
            self.rootdir = os.path.realpath('.')
            self.tempfile = "_youtube-dl_%s.flv" % randint(10000, 99999)
            self.targetdir = '%s\\Downloads' % os.environ['USERPROFILE']
            os.chdir(self.targetdir)
    
    def fetch(self):
        """
        The fetch() method is equivalent to running the following shell commands:
        
        youtube-dl --output=$tempfile --format=18 $youtubeurl
        ffmpeg -i $tempfile -acodec libmp3lame -ac 2 -ab 128k -vn -y "$songname"
        
        UPDATE: 20150830 - Zenmp3 will now advocate freedom for the user in the
        form of free file formats. This means that we now download into OGG 
        audio by default. Should the user want to use MP3 instead, there will 
        be an option there. Hence, this method is now equivalent to:

        youtube-dl --output=$tempfile --format=18 $youtubeurl
        ffmpeg -i "$tempfile" -vn -acodec libvorbis -y "${songname}.ogg"
        """
        print("Fetching the song...")
        if OS_TYPE == 'linux' or OS_TYPE == 'mac':
            self.fetcher = sp.Popen(['youtube-dl', '--output=%s' % self.tempfile, '--format=18','%s' % self.address], stdout=sp.PIPE)
            if hasGTK:
                while self.fetcher.poll() == None:
                    # 'None' means not finished; we can update our GUI in the
                    # meantime. Also, the testing of this loop means that the
                    # program will not carry on the next steps until we're done
                    # TODO: Implement a way of telling the user how far we are,
                    # like progress bars, bytes transferred, etc.
                    while gtk.events_pending():
                        gtk.main_iteration()
            else:
                # I still don't know how to implement this GUI updates in Tk.
                # This is the best that they can get for now...
                self.fetcher.wait()
        else:
            self.fetcher = sp.Popen(['%s\\win32\\bin\\youtube-dl.exe' % self.rootdir, '--output=%s' % self.tempfile, '--format=18','%s' % self.address], stdout=sp.PIPE)
            self.fetcher.wait()
        print("Converting the song...")
        if OS_TYPE == 'linux' or OS_TYPE == 'mac':
            if hasGTK:
                interface.statusbar.set_text("Converting the song...")
            elif hasTK:
                interface.statusbar.config(text = "Converting the song...")
            if self.use_mp3:
                self.converter = sp.Popen([converter, '-i', '%s' % self.tempfile, '-acodec', 
                    'libmp3lame', '-ac', '2','-ab', '128k', '-vn', '-y', 
                    '%s' % self.target], stdout=sp.PIPE, stderr=sp.PIPE)
            else:
                self.converter = sp.Popen([converter, '-i', '%s' % self.tempfile, '-vn', 
                    '-acodec', 'libvorbis', '-y', '%s' % self.target], 
                    stdout=sp.PIPE, stderr=sp.PIPE)

            if hasGTK:
                while self.converter.poll() == None:
                    while gtk.events_pending():
                        gtk.main_iteration()
            else:
                self.converter.wait()
        else:
            self.converter = sp.Popen(['%s\\win32\\bin\\ffmpeg.exe'% self.rootdir, '-i', '%s' % self.tempfile, '-acodec', 'libmp3lame', '-ac', '2','-ab', '128k', '-vn', '-y', '%s' % self.target], stdout=sp.PIPE, stderr=sp.PIPE)
            self.converter.wait()
    def clean(self):
        if OS_TYPE == 'linux' or OS_TYPE == 'mac':
            os.remove(self.targetdir + self.tempfile)
        elif OS_TYPE == 'windows':
            os.remove(self.tempfile)

class Downloader():
    '''
    Our downloader window.
    
    This window will analize the inputs (to as much as it 
    can) to see if the inputs are ok (i.e. if they exist and
    if it's an url with http and such, and then perform the 
    download if possible, otherwise, tell the user to try again.
    '''
    if hasGTK:
        '''
        This is the de facto, standard Zenmp3 interface. This is the 
        interface that should be used in case the machine has more than
        one widget toolkit installed.
        
        Most Linux distributions with GNOME or GTKish environments will use it
        without problem, but occasionally someone with KDE will pop in.
        That's why we have the other fallback toolkits to help us out.
        '''
        def fetcher(self, widget, data=None):
            self.url = self.urlentry.get_text()
            self.filename = self.nameentry.get_text()
            if self.url == '':
                sys.stderr.write("Didn't quite get that.\n")
            elif self.url.startswith('http://') or self.url.startswith('https://'):
                pass
            else:
                self.url = 'http://' + self.url
            
            if self.filename == '' or self.filename == ' ':
                sys.stderr.write("No filename provided!\n")
            
            if self.filename != "" and self.url != "":
                # We are good to go!
                print(u"The data sent was:\n%s\n%s" % (self.url, self.filename))
                self.statusbar.set_text("Now downloading the video...")
                dl = Download(OS_TYPE, self.url, self.filename, 
                    self.destinationpath.get_text(), self.togglemp3.get_active())
                self.urlentry.set_text('')
                self.nameentry.set_text('')
                self.fetcherButton.set_sensitive(False)
                dl.fetch()
                if dl.target in os.listdir(dl.targetdir):
                    print('\n\n--> Song "%s" was successfully downloaded to: %s' % (dl.target, dl.targetdir))
                    dl.clean()
                    self.statusbar.set_text("Song downloaded to %s" % self.destinationpath.get_text())
                else:
                    self.statusbar.set_text("There was an error. Your song could not be downloaded.")
                    print("There was an error. Your song could not be downloaded.")
                    print(dl.converter.communicate()[1])
                    self.fetcherButton.set_sensitive(True) # Try again.
                    dl.clean()
            else:
                self.statusbar.set_text("Error: please provide a URL and a file name.")
            
            # Restore everything
            self.fetcherButton.set_sensitive(True)
            
        def delete_event(self, widget, data):
            gtk.main_quit()
            return False

        def cd(self, widget, data=None):
            "Change the saving directory through a GUI selector"
            self.dialog = gtk.FileChooserDialog(
                title = "Choose a target directory",
                parent = self.window,
                action = gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER,
                buttons =  (
                    gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
                    gtk.STOCK_OK, gtk.RESPONSE_OK
                )
            )
            self.savedirectory = self.dialog.run()
            if self.savedirectory == gtk.RESPONSE_OK:
                print("Destination set to: %s" % self.dialog.get_filename())
                self.destinationpath.set_text(self.dialog.get_filename())
            else:
                print("Folder selection cancelled.")
            self.dialog.destroy()

        def is_MP3(self, widget, data=None):
            '''
            Test it: did we toggle the MP3 switch?
            
            Returns bool
            '''
            if self.togglemp3.get_active():
                print("You switched to MP3 mode")
            else:
                print("You switched to OGG mode")
            return self.togglemp3.get_active()

        def search(self, widget, data=None):
            '''
            Interface to the search_song() function.
            '''
            token = self.searchentry.get_text()
            print("Searching for %s" % token)
            results = search_song(token)

            print("Search finished.")
            temp = ""
            for res in results:
                # fill the textbox with the found results.
                temp += "%s -> %s\n" % (res['title'], res['url'])

            self.textbuffer.set_text(temp)
            
        def __init__(self):
            # Preprocessor variables for the GUI:
            self.elementList = []
            self.savedirectory = os.environ["HOME"]
 
            # Interface overlay
            self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
            self.window.set_title("ZenMP3 - YouTube Music Downloader")
            self.window.set_default_size(440, 120)
            self.window.set_border_width(10)
            self.fetcherButton = gtk.Button("_Download song")
            self.elementList.append(self.fetcherButton)
            self.grid = gtk.VBox(False, 0)
            self.elementList.append(self.grid)
            self.urlfield = gtk.HBox(False, 0)
            self.elementList.append(self.urlfield)
            self.urlentry = gtk.Entry()
            self.elementList.append(self.urlentry)
            self.urllabel = gtk.Label("Video URL:")
            self.elementList.append(self.urllabel)
            self.namefield = gtk.HBox(False, 0)
            self.elementList.append(self.namefield)
            self.nameentry = gtk.Entry()
            self.elementList.append(self.nameentry)
            self.namelabel = gtk.Label("Filename:")
            self.elementList.append(self.namelabel)
            self.destinationfield = gtk.HBox(False, 0)
            self.elementList.append(self.destinationfield)
            self.destinationlabel = gtk.Label("Destination:")
            self.elementList.append(self.destinationlabel)
            self.destinationpath = gtk.Entry()
            self.destinationpath.set_text(self.savedirectory)
            self.elementList.append(self.destinationpath)
            self.destinationchange = gtk.Button("_Change")
            self.elementList.append(self.destinationchange)
            self.statusbar = gtk.Label("Ready")
            self.elementList.append(self.statusbar)

            # Search bar to find music. Could it be?
            self.searchfield = gtk.HBox(False, 0)
            self.elementList.append(self.searchfield)
            self.searchentry = gtk.Entry()
            self.elementList.append(self.searchentry)
            self.searchbutton = gtk.Button("_Search for music")
            self.elementList.append(self.searchbutton)

            # This field will host the results you find by searching:
            self.textbox = gtk.TextView()
            self.textbox.set_wrap_mode(gtk.WRAP_WORD)
            self.elementList.append(self.textbox)

            # Checkbutton that will fallback to mp3:
            self.togglemp3 = gtk.CheckButton("Use _MP3")
            self.elementList.append(self.togglemp3)
            self.mp3expl = gtk.Label("By default, Zenmp3 uses OGG. Check this box if you would like to use MP3 instead.")
            self.elementList.append(self.mp3expl)
            self.mp3box = gtk.HBox(False, 0)
            self.elementList.append(self.mp3box)

            # Set the interaction logic
            self.window.connect("destroy", gtk.main_quit)
            self.fetcherButton.connect("clicked", self.fetcher)
            self.destinationchange.connect("clicked", self.cd)
            self.togglemp3.connect("clicked", self.is_MP3)
            self.searchbutton.connect("clicked", self.search)

            # This is the "writer" that will load results into the interface:
            self.textbuffer = self.textbox.get_buffer()
            
            # Show all elements
            for element in self.elementList:
                element.show()
            
            self.urlfield.pack_start(self.urllabel, False, False)
            self.urlfield.pack_start(self.urlentry, True, True)
 
            self.namefield.pack_start(self.namelabel, False, False)
            self.namefield.pack_start(self.nameentry, True, True)
 
            self.destinationfield.pack_start(self.destinationlabel, False, False)
            self.destinationfield.pack_start(self.destinationpath, True, True)
            self.destinationfield.pack_start(self.destinationchange, False, False)

            self.mp3box.pack_start(self.mp3expl, True, True)
            self.mp3box.pack_start(self.togglemp3, False, False)

            self.searchfield.pack_start(self.searchentry, True, True)
            self.searchfield.pack_start(self.searchbutton, False, False)
 
            self.grid.pack_start(self.urlfield, True, False)
            self.grid.pack_start(self.namefield, True, False)
            self.grid.pack_start(self.destinationfield, True, False)
            self.grid.pack_start(self.searchfield, True, False)
            self.grid.pack_start(self.mp3box, True, False)
            self.grid.pack_start(self.fetcherButton, False, False)
            self.grid.pack_start(self.textbox, True, True)
            self.grid.pack_start(self.statusbar, True, True)
 
            self.window.add(self.grid)
            self.window.show()

            # Warn the user if their youtube-dl isn't updated enough:
            self.fetcherButton.set_sensitive(False)
            self.statusbar.set_text("Checking youtube-dl version...")
            self.statusbar.set_text(warn_old_version())
            self.fetcherButton.set_sensitive(True)

        def main(self):
            gtk.main()
    
    elif hasTK:
        '''
        If we can't use GTK, we fall back to the next preferred toolkit.
        And so on if we can't use this toolkit either.
        
        This section is important because sometimes Windows installs 
        cannot make use of PyGTK, even if is installed.
        
        The tkinter class is, therefore, a security blanket to allow
        Windows users with default installations to use the graphical
        user interface
        '''
        def fetcher(self):
            self.url = self.urlfield.get()
            self.filename = self.namefield.get()
            if self.url == '' or self.filename == '':
                sys.stderr.write("Missing arguments: unable to proceed.\n")
                self.statusbar.config(text="Missing arguments.", fg="#F00")
            else:
                if self.url.startswith('http://') or self.url.startswith('https://'):
                    pass
                else:
                    self.url = 'http://' + self.url
                if self.filename.endswith('.mp3'):
                    pass
                else:
                    self.filename += '.mp3'
                self.statusbar.config(text="Now fetching from: %s." % self.url, fg="#000")
                self.urlfield.delete(0, tk.END)
                self.namefield.delete(0, tk.END)
                print(u"The data sent was:\n%s\n%s" % (self.url, self.filename))
                dl = Download(OS_TYPE, self.url, self.filename, os.environ['HOME'])
                dl.fetch()
                if dl.target in os.listdir(dl.targetdir):
                    print('\n\n--> Song "%s" was successfully downloaded to: %s' % (dl.target, dl.targetdir))
                    dl.clean()
                    self.statusbar.config(text="Song '%s' was successfully downloaded to: %s" % (dl.target, dl.targetdir), fg="#05AD2F")
                else:
                    print("There was an error. Your song could not be downloaded.")
                    print(dl.converter.communicate()[1])
                    self.statusbar.config(text="There was an error. Your song could not be downloaded.", fg="#F00")
                    dl.clean()
                
        def givecredit(self):
            print("Authored by Klaus Zimmermann - 2013\nhttp://notabug.com/kzimmerman/zenmp3")
            self.statusbar.config(text="Authored by Klaus Zimmermann - 2013 - http://notabug.com/kzimmerman/zenmp3", fg="#000")
            
        def onlinehelp(self):
            webbrowser.open('http://notabug.com/kzimmerman/zenmp3/')
            
        def __init__(self):
            # Add overlay
            self.window = tk.Tk()
            self.window.geometry('500x150')
            self.field1 = tk.Frame(self.window)
            self.field2 = tk.Frame(self.window)
            self.label = tk.Label(text="Welcome to Zenmp3.\nTo download a song, please enter an URL and a name for it.")
            self.label2 = tk.Label(self.field1, text="YouTube URL: ")
            self.label3 = tk.Label(self.field2, text="Song name: ")
            self.urlfield = tk.Entry(self.field1)
            self.namefield = tk.Entry(self.field2)
            self.statusbar = tk.Label(self.window, text="")
            self.button = tk.Button(text="Fetch song", command=self.fetcher, bg="#F5CB9A")
            self.credits = tk.Button(text="Credits", command=self.givecredit)
            self.helper = tk.Button(text="Help", command=self.onlinehelp)
            
            # Pack everything neatly
            self.window.title("Zenmp3 - YouTube to MP3 converter - Version %s" % VERSION)
            self.label.pack(expand=1, fill='x')
            self.field1.pack( fill='x')
            self.field2.pack( fill='x')
            self.label2.pack(in_=self.field1, side='left')
            self.urlfield.pack(in_=self.field1, fill="x", side='left', expand=1, padx=5)
            self.label3.pack(in_=self.field2, side='left')
            self.namefield.pack(in_=self.field2, fill="x",side='left', expand=1, padx=5)
            self.statusbar.pack(in_=self.window)
            self.button.pack( side='right', ipadx=5, ipady=5, padx=5, pady=5)
            self.credits.pack( side='left', ipadx=5, ipady=5, padx=5, pady=5)
            self.helper.pack( side='left', ipadx=5, ipady=5, padx=5, pady=5)
            
        def main(self):
            self.window.mainloop()

    else:
        def __init__(self):
            print("Sorry! Yet to implement it on other toolkits!")
            sys.exit(2)

if __name__ == "__main__":
    if hasgui:
        if sanitycheck():
            interface = Downloader()
            interface.main()
        else:
            print("There are unmet dependencies in your computer.\nPlease see http://notabug.com/kzimmerman/zenmp3 for more information.")
            sys.exit(1)
    else:
        dl = Download(OS_TYPE, URL, SONGNAME)
        if sanitycheck():
            dl.fetch()
            if dl.target in os.listdir(dl.targetdir):
                print('\n\n--> Song "%s" was successfully downloaded to: %s' % (dl.target, dl.targetdir))
                dl.clean()
            else:
                print("There was an error. Your song could not be downloaded.")
                print(dl.converter.communicate()[1])
                dl.clean()
            sys.exit(0)
        else:
            print("There are unmet dependencies in your computer.")
            print("Please see http://notabug.com/kzimmerman/zenmp3 for more information.")
            sys.exit(1)
